package com.atguigu.gmall.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.common.constant.MqConst;
import com.atguigu.gmall.common.service.RabbitService;
import com.atguigu.gmall.common.util.HttpClientUtil;
import com.atguigu.gmall.model.enums.OrderStatus;
import com.atguigu.gmall.model.enums.ProcessStatus;
import com.atguigu.gmall.model.order.OrderDetail;
import com.atguigu.gmall.model.order.OrderInfo;
import com.atguigu.gmall.order.mapper.OrderDetailMapper;
import com.atguigu.gmall.order.mapper.OrderInfoMapper;
import com.atguigu.gmall.order.service.OrderService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * ClassName: OrderInfoServiceImpl
 * Package: com.atguigu.gmall.order.service.impl
 * Description:
 *
 * @Author 刘轩
 * @Create 2023-09-02 15:37
 * @Version 1.0
 */

@Service
public class OrderServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderService {


    @Autowired
    private OrderInfoMapper orderInfoMapper;

    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RabbitService rabbitService;


    /*
 ware:
     url: http://localhost:9001
  */
    //  获取仓库地址
    @Value("${ware.url}")
    private String wareUrl;

    /**
     * 提交订单
     *
     * @param orderInfo
     * @param request
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long saveOrderInfo(OrderInfo orderInfo) {
        //  order_info order_detail
        // total_amount order_status user_id out_trade_no trade_body operate_time expire_time process_status
        orderInfo.sumTotalAmount();
        orderInfo.setOrderStatus(OrderStatus.UNPAID.name());
        String outTradeNo = "ATGUIGU" + System.currentTimeMillis() + new Random().nextInt(1000);
        orderInfo.setOutTradeNo(outTradeNo);
        orderInfo.setTradeBody("购买商品");
        orderInfo.setOperateTime(new Date());
        //  过期时间如何处理? 当前系统时间上+1天 ,根商品的库存剩余数量有关系?
        //  Calendar calendar = Calendar.getInstance();
        //  calendar.add(Calendar.DATE,1);
        //  orderInfo.setExpireTime(calendar.getTime());
        //  LocalTime LocalDate LocalDateTime
        //  如何获取到当前系统时间; LocalDateTime.now()
        //  在当前系统时间基础上 +1 天
        LocalDateTime localDateTime = LocalDateTime.now().plusDays(1);
        ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.systemDefault());
        /*
        如果是 JDK8 的应用，可以使用 Instant 代替 Date，LocalDateTime 代替 Calendar，DateTimeFormatter 代替
            SimpleDateFormat，官方给出的解释：simple beautiful strong immutable thread-safe。
         */
        orderInfo.setExpireTime(Date.from(zonedDateTime.toInstant()));
        //  订单的进度状态
        orderInfo.setProcessStatus(ProcessStatus.UNPAID.name());
        //  保存订单主信息
        orderInfoMapper.insert(orderInfo);

        //  获取到订单明细
        List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
        //  循环遍历
        if (!CollectionUtils.isEmpty(orderDetailList)) {
            for (OrderDetail orderDetail : orderDetailList) {
                //  赋值订单Id
                orderDetail.setOrderId(orderInfo.getId());
                orderDetailMapper.insert(orderDetail);
            }
        }
        // 发送一个延迟消息
        rabbitService.sendDelayMsg(MqConst.EXCHANGE_DIRECT_ORDER_CANCEL, MqConst.ROUTING_ORDER_CANCEL, orderInfo.getId(), MqConst.DELAY_TIME);
        return orderInfo.getId();
    }

    /**
     * 取消订单
     *
     * @param orderId
     */
    @Override
    public void execExpiredOrder(Long orderId) {

        //  根据订单Id 将订单状态改为 CLOSED.
        //  根据订单Id 将订单状态改为 PAID.
        //  根据订单Id 将订单状态改为 xxx.
        this.updateOrderStatus(orderId, ProcessStatus.CLOSED);
//        this.updateOrderStatus(orderId,ProcessStatus.PAID);
//        this.updateOrderStatus(orderId,ProcessStatus.DELEVERED);
        // 发送消息给payment——service 让其关闭paymentinfo
        rabbitService.sendMsg(MqConst.EXCHANGE_DIRECT_PAYMENT_CLOSE, MqConst.ROUTING_PAYMENT_CLOSE, orderId);
    }

    /**
     * 修改订单
     *
     * @param orderId
     * @param closed
     */
    @Override
    public void updateOrderStatus(Long orderId, ProcessStatus processStatus) {
        // 取消订单本质:修改订单状态，获取订单状态
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setId(orderId);
        orderInfo.setOrderStatus(processStatus.getOrderStatus().name());
        orderInfo.setProcessStatus(processStatus.CLOSED.name());
        orderInfoMapper.updateById(orderInfo);
    }


    /**
     * 根据订单Id 获取数据
     *
     * @param orderId
     * @return
     */
    @Override
    public OrderInfo getOrderInfo(Long orderId) {
        //  先获取订单信息
        OrderInfo orderInfo = this.getById(orderId);
        //  判断
        if (null != orderInfo) {
            //  获取到订单明细
            List<OrderDetail> orderDetailList = this.orderDetailMapper.selectList(new LambdaQueryWrapper<OrderDetail>().eq(OrderDetail::getOrderId, orderId));
            orderInfo.setOrderDetailList(orderDetailList);
        }
        return orderInfo;
    }

    /**
     * 发消息给库存
     *
     * @param orderId
     */
    @Override
    public void senMsgToWare(Long orderId) {
        //  订单这个状态要更新
        this.updateOrderStatus(orderId, ProcessStatus.NOTIFIED_WARE);
        //  发送的内容由订单+订单明细组成
        OrderInfo orderInfo = this.getOrderInfo(orderId);
        //  将订单对象转换为一个map 或JSONObject 对象。
        Map map = this.initWare(orderInfo);
        //  将map 转换json 发送出去
        this.rabbitService.sendMsg(MqConst.EXCHANGE_DIRECT_WARE_STOCK, MqConst.ROUTING_WARE_STOCK, JSON.toJSONString(map));
    }

    /**
     * 将订单对象转换为map 集合
     *
     * @param orderInfo
     * @return
     */
    @Override
    public Map initWare(OrderInfo orderInfo) {
        //  创建map 对象
        HashMap<String, Object> hashMap = new HashMap<>();
        hashMap.put("orderId", orderInfo.getId());
        hashMap.put("consignee", orderInfo.getConsignee());
        hashMap.put("consigneeTel", orderInfo.getConsigneeTel());
        hashMap.put("orderComment", orderInfo.getOrderComment());
        hashMap.put("orderBody", orderInfo.getTradeBody());
        hashMap.put("deliveryAddress", orderInfo.getDeliveryAddress());
        hashMap.put("paymentWay", "2");
        hashMap.put("wareId", orderInfo.getWareId());

        //  获取到订单明细.
        List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
        if (!CollectionUtils.isEmpty(orderDetailList)) {
            List<HashMap<String, Object>> mapList = orderDetailList.stream().map(orderDetail -> {
                //  创建map 对象
                HashMap<String, Object> map = new HashMap<>();
                map.put("skuId", orderDetail.getSkuId());
                map.put("skuNum", orderDetail.getSkuNum());
                map.put("skuName", orderDetail.getSkuName());
                return map;
            }).collect(Collectors.toList());
            hashMap.put("details", mapList);
        }
        return hashMap;
    }

// 获取流水号

    @Override
    public String getTradeNo(String userId) {
        //  生成流水号
        String tradeNo = UUID.randomUUID().toString();
        //  同时将这个流水号存储到缓存.
        String tradeNoKey = "tradeNo:" + userId;
        //  存储
        this.redisTemplate.opsForValue().set(tradeNoKey, tradeNo);
        return tradeNo;
    }

    /**
     * 比较流水号
     *
     * @param tradeNo 页面传递
     * @param userId  获取缓存
     * @return
     */
    @Override
    public Boolean checkTradeNo(String tradeNo, String userId) {
        //  同时将这个流水号存储到缓存.
        String tradeNoKey = "tradeNo:" + userId;
        String redisTradeNo = (String) this.redisTemplate.opsForValue().get(tradeNoKey);
        //  返回比较结果
        return tradeNo.equals(redisTradeNo);
    }

    /**
     * 删除流水号
     *
     * @param userId
     */
    @Override
    public void delTradeNo(String userId) {
        redisTemplate.delete("tradeNo:" + userId);
    }

    /**
     * 校验库存
     *
     * @param skuId
     * @param skuNum
     * @return
     */
    @Override
    public Boolean checkStock(Long skuId, Integer skuNum) {
        //  http://localhost:9001/hasStock?skuId=10221&num=2
        String flag = HttpClientUtil.doGet(wareUrl + "/hasStock?skuId=" + skuId + "&num=" + skuNum);
        //  返回结果
        return "1".equals(flag);
    }

    @Override
    public IPage<OrderInfo> getOrderPageList(Page<OrderInfo> orderInfoPage, String userId, String orderStatus) {
        //  调用mapper方法
        IPage<OrderInfo> infoIPage = orderInfoMapper.selectOrderPageList(orderInfoPage, userId, orderStatus);
        infoIPage.getRecords().forEach(orderInfo -> {
            //  给订单状态名称赋值  UNPAID--->未支付  PAID--支付
            orderInfo.setOrderStatusName(OrderStatus.getStatusNameByStatus(orderInfo.getOrderStatus()));
        });
        return infoIPage;
    }


    /**
     * 拆单业务逻辑
     *
     * @param orderId
     * @param wareSkuMap
     * @return
     */
    @Override
    public List<OrderInfo> orderSpilt(String orderId, String wareSkuMap) {

            /*
        1.  先获取到原始订单： 要被拆分的订单
        2.  将wareSkuMap 转换为我们能操作的java 对象 [{"wareId":"1","skuIds":["26"]},{"wareId":"2","skuIds":["29","30"]}]
        3.  创建新的子订单并给子订单进行赋值
        4.  将子订单添加到集合中
        5.  包子订单
        6。 修改原始订单状态
         */
        //  声明一个子订单集合
        List<OrderInfo> orderInfoList = new ArrayList<>();
        //  原始订单
        OrderInfo originOrderInfo = this.getOrderInfo(Long.parseLong(orderId));
        // 转换为list<Map>
        List<Map> mapList = JSON.parseArray(wareSkuMap, Map.class);
        // 判断
        if (!CollectionUtils.isEmpty(mapList)) {
            // 循环遍历
            for (Map map : mapList) {
                //  获取仓库Id
                String wareId = (String) map.get("wareId");
                List<String> skuIdList = (List<String>) map.get("skuIds");
                // 创建一个新的子订单并给子订单赋值
                OrderInfo subOrderInfo = new OrderInfo();
                //  属性拷贝
                BeanUtils.copyProperties(originOrderInfo, subOrderInfo);
                //  设置Id 为null 防止主键冲突
                subOrderInfo.setId(null);
                subOrderInfo.setParentOrderId(Long.parseLong(orderId));
                // 赋值仓库id
                subOrderInfo.setWareId(wareId);
                //  计算子订单明细 -- 来自于原始订单明细. originOrderInfo.getOrderDetailList(); 26 29 30
                List<OrderDetail> orderDetailList = originOrderInfo.getOrderDetailList();
                //  [{"wareId":"1","skuIds":["26"]},{"wareId":"2","skuIds":["29","30"]}]
                //  第一次循环 {"wareId":"1","skuIds":["26"]} 第二次循环 {"wareId":"2","skuIds":["29","30"]}
                //  子订单明细集合
                List<OrderDetail> detailList = orderDetailList.stream().filter(orderDetail -> skuIdList.contains(orderDetail.getSkuId().toString())).collect(Collectors.toList());
                subOrderInfo.setOrderDetailList(detailList);
                //  计算子订单金额
                subOrderInfo.sumTotalAmount();
                //  添加子订单到集合
                orderInfoList.add(subOrderInfo);
                //  保存子订单.
                this.saveOrderInfo(subOrderInfo);
            }
        }
        // 修改原始订单状态
        updateOrderStatus(Long.parseLong(orderId), ProcessStatus.SPLIT);
        // 返回子订单结合
        return orderInfoList;
    }

    /**
     * 取消订单
     *
     * @param orderId
     * @param flag
     */
    @Override
    public void execExpiredOrder(Long orderId, String flag) {

        //  根据订单Id 将订单状态改为 CLOSED.
        this.updateOrderStatus(orderId, ProcessStatus.CLOSED);
        if ("2".equals(flag)) {
            // 发送消息给payment——service 让其关闭paymentinfo
            rabbitService.sendMsg(MqConst.EXCHANGE_DIRECT_PAYMENT_CLOSE, MqConst.ROUTING_PAYMENT_CLOSE, orderId);

        }
    }
}
